﻿using System;
using static System.Collections.Specialized.BitVector32;

public enum VariantType
{
    NONE,
    BOOL,
    INT8,
    INT16,
    INT32,
    INT64,
    UINT8,
    UINT16,
    UINT32,
    UINT64,
    FLOAT,
    DOUBLE,
    STRING,
    OBJECT,
    ACTION,
}

public struct Variant
{
    public VariantType type;
    private long n_value;
    private double d_value;
    private string str_value;
    private object obj_value;
    private Action act_value;

    public static Variant None = new Variant();

    public Variant(bool value)
    {
        type = VariantType.BOOL;
        n_value = value ? 1 : 0;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;
    }

    public Variant(sbyte value)
    {
        type = VariantType.INT8;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;
    }

    public Variant(short value)
    {
        type = VariantType.INT16;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;
    }

    public Variant(int value)
    {
        type = VariantType.INT32;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;
    }

    public Variant(long value)
    {
        type = VariantType.INT64;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;
    }

    public Variant(byte value)
    {
        type = VariantType.UINT8;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
            act_value = null;
    }

    public Variant(ushort value)
    {
        type = VariantType.UINT16;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
            act_value = null;
    }

    public Variant(uint value)
    {
        type = VariantType.UINT32;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;

    }

    public Variant(ulong value)
    {
        type = VariantType.UINT64;
        n_value = (long)value;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;

    }

    public Variant(float value)
    {
        type = VariantType.FLOAT;
        n_value = 0;
        d_value = value;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;

    }

    public Variant(double value)
    {
        type = VariantType.DOUBLE;
        n_value = 0;
        d_value = value;
        str_value = string.Empty;
        obj_value = null;
        act_value = null;

    }

    public Variant(string value)
    {
        type = VariantType.STRING;
        n_value = 0;
        d_value = 0.0;

        if (string.IsNullOrEmpty(value))
            str_value = string.Empty;
        else
            str_value = value;
        obj_value = null;
        act_value = null;

    }


    public Variant(Action value)
    {
        type = VariantType.ACTION;
        n_value = 0;
        d_value = 0.0;
        str_value = string.Empty;
        obj_value = null;
        act_value = value;

    }


    public static Variant MakeForBool(bool value)
    {
        Variant v = new Variant();
        v.type = VariantType.BOOL;
        v.n_value = value ? 1 : 0;
        return v;
    }

    public static Variant MakeForInt8(sbyte value)
    {
        Variant v = new Variant();
        v.type = VariantType.INT8;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForInt16(short value)
    {
        Variant v = new Variant();
        v.type = VariantType.INT16;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForInt32(int value)
    {
        Variant v = new Variant();
        v.type = VariantType.INT32;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForInt(int value)
    {
        Variant v = new Variant();
        v.type = VariantType.INT32;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForInt64(long value)
    {
        Variant v = new Variant();
        v.type = VariantType.INT64;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForLong(long value)
    {
        Variant v = new Variant();
        v.type = VariantType.INT64;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForByte(byte value)
    {
        Variant v = new Variant();
        v.type = VariantType.UINT8;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForUint8(byte value)
    {
        Variant v = new Variant();
        v.type = VariantType.UINT8;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForUint16(ushort value)
    {
        Variant v = new Variant();
        v.type = VariantType.UINT16;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForUint32(uint value)
    {
        Variant v = new Variant();
        v.type = VariantType.UINT32;
        v.n_value = value;
        return v;
    }

    public static Variant MakeForUint64(ulong value)
    {
        Variant v = new Variant();
        v.type = VariantType.UINT64;
        v.n_value = (long)value;
        return v;
    }

    public static Variant MakeForFloat(float value)
    {
        Variant v = new Variant();
        v.type = VariantType.DOUBLE;
        v.d_value = value;
        return v;
    }

    public static Variant MakeForDouble(double value)
    {
        Variant v = new Variant();
        v.type = VariantType.DOUBLE;
        v.d_value = value;
        return v;
    }

    public static Variant MakeForString(string value)
    {
        Variant v = new Variant();
        v.type = VariantType.STRING;
        v.str_value = string.IsNullOrEmpty(value) ? string.Empty : value;
        return v;
    }



    public static Variant MakeForAction(Action value)
    {
        Variant v = new Variant();
        v.type = VariantType.ACTION;
        v.act_value = value;
        return v;
    }


    public static Variant MakeForObject(object obj)
    {
        Variant v = new Variant();

        if (obj is int)
        {
            v.type = VariantType.INT32;
            v.n_value = (int)obj;
        }
        else if (obj is uint)
        {
            v.type = VariantType.UINT32;
            v.n_value = (uint)obj;
        }
        else if (obj is byte)
        {
            v.type = VariantType.INT8;
            v.n_value = (byte)obj;
        }
        else if (obj is sbyte)
        {
            v.type = VariantType.UINT8;
            v.n_value = (sbyte)obj;
        }
        else if (obj is bool)
        {
            v.type = VariantType.BOOL;
            v.n_value = (bool)obj ? 1 : 0;
        }
        else if (obj is short)
        {
            v.type = VariantType.INT16;
            v.n_value = (short)obj;
        }
        else if (obj is ushort)
        {
            v.type = VariantType.UINT16;
            v.n_value = (ushort)obj;
        }
        else if (obj is long)
        {
            v.type = VariantType.INT64;
            v.n_value = (long)obj;
        }
        else if (obj is ulong)
        {
            v.type = VariantType.UINT64;
            v.n_value = (long)(ulong)obj;
        }
        else if (obj is float)
        {
            v.type = VariantType.FLOAT;
            v.d_value = (float)obj;
        }
        else if (obj is double)
        {
            v.type = VariantType.DOUBLE;
            v.d_value = (double)obj;
        }
        else if (obj is string)
        {
            v.type = VariantType.STRING;
            v.str_value = (string)obj;
        }
        else if (obj is Action)
        {
            v.type = VariantType.ACTION;
            v.act_value = (Action)obj;
        }
        else
        {
            v.type = VariantType.OBJECT;
            v.obj_value = obj;
        }
        return v;
    }

    public bool IsEmpty()
    {
        return VariantType.NONE == type;
    }

    public bool IsNull()
    {
        return VariantType.NONE == type || (VariantType.OBJECT == type && null == obj_value);
    }

    public bool IsValid()
    {
        return VariantType.NONE != type;
    }

    public bool IsInt()
    {
        return VariantType.INT8 == type || VariantType.INT16 == type || VariantType.INT32 == type || VariantType.INT64 == type
            || VariantType.UINT8 == type || VariantType.UINT16 == type || VariantType.UINT32 == type || VariantType.UINT64 == type;
    }

    public bool IsInt64()
    {
        return VariantType.INT64 == type || VariantType.UINT64 == type;
    }

    public bool IsInt32()
    {
        return VariantType.INT8 == type || VariantType.INT16 == type || VariantType.INT32 == type
            || VariantType.UINT8 == type || VariantType.UINT16 == type || VariantType.UINT32 == type;
    }

    public bool IsNumber()
    {
        return VariantType.INT8 == type || VariantType.INT16 == type || VariantType.INT32 == type || VariantType.INT64 == type
            || VariantType.UINT8 == type || VariantType.UINT16 == type || VariantType.UINT32 == type || VariantType.UINT64 == type
            || VariantType.FLOAT == type || VariantType.DOUBLE == type;
    }

    public bool IsFloatNumber()
    {
        return VariantType.FLOAT == type || VariantType.DOUBLE == type;
    }

    public void SetBool(bool value)
    {
        type = VariantType.BOOL;
        n_value = value ? 1 : 0;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetInt8(sbyte value)
    {
        type = VariantType.INT8;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetInt16(short value)
    {
        type = VariantType.INT16;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetInt32(int value)
    {
        type = VariantType.INT32;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetInt64(long value)
    {
        type = VariantType.INT64;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetUint8(byte value)
    {
        type = VariantType.UINT8;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetUint16(ushort value)
    {
        type = VariantType.UINT16;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetUint32(uint value)
    {
        type = VariantType.UINT32;
        n_value = value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetUint64(ulong value)
    {
        type = VariantType.UINT64;
        n_value = (long)value;
        d_value = 0.0;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetFloat(float value)
    {
        type = VariantType.DOUBLE;
        n_value = 0;
        d_value = value;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetDouble(double value)
    {
        type = VariantType.DOUBLE;
        n_value = 0;
        d_value = value;
        str_value = string.Empty;

        obj_value = null;
    }

    public void SetString(string value)
    {
        type = VariantType.STRING;
        n_value = 0;
        d_value = 0.0;

        if (string.IsNullOrEmpty(value))
            str_value = string.Empty;
        else
            str_value = value;

        obj_value = null;
    }

    public bool GetBool()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return n_value != 0;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return d_value != 0;
            case VariantType.STRING:
                return GameConvert.BoolConvert(str_value);
            default:
                return false;
        }
    }

    public double GetDouble()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return d_value;
            case VariantType.STRING:
                return GameConvert.DoubleConvert(str_value);
            default:
                return 0.0;
        }
    }

    public float GetFloat()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (float)d_value;
            case VariantType.STRING:
                return GameConvert.FloatConvert(str_value);
            default:
                return 0.0f;
        }
    }

    public short GetInt16()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (short)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (short)d_value;
            case VariantType.STRING:
                return (short)GameConvert.IntConvert(str_value);
            default:
                return (short)0;
        }
    }

    public int GetInt32()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (int)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (int)d_value;
            case VariantType.STRING:
                return (int)GameConvert.IntConvert(str_value);
            default:
                return (int)0;
        }
    }

    public int GetInt()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (int)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (int)d_value;
            case VariantType.STRING:
                return (int)GameConvert.IntConvert(str_value);
            default:
                return (int)0;
        }
    }

    public long GetLong()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (long)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (long)d_value;
            case VariantType.STRING:
                return (long)GameConvert.IntConvert(str_value);
            default:
                return (long)0;
        }
    }

    public long GetInt64()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (long)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (long)d_value;
            case VariantType.STRING:
                return (long)GameConvert.IntConvert(str_value);
            default:
                return (long)0;
        }
    }

    public sbyte GetInt8()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (sbyte)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (sbyte)d_value;
            case VariantType.STRING:
                return (sbyte)GameConvert.IntConvert(str_value);
            default:
                return (sbyte)0;
        }
    }

    public Action GetAction()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
            case VariantType.STRING:
            case VariantType.ACTION:
                return (Action)act_value;
            default:
                return null;
        }
    }





    public object GetNull()
    {
        return obj_value;
    }


    public string GetString()
    {
        return str_value;
    }

    public ushort GetUInt16()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (ushort)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (ushort)d_value;
            case VariantType.STRING:
                return (ushort)GameConvert.IntConvert(str_value);
            default:
                return (ushort)0;
        }
    }

    public uint GetUInt32()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (uint)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (uint)d_value;
            case VariantType.STRING:
                return (uint)GameConvert.IntConvert(str_value);
            default:
                return (uint)0;
        }
    }

    public ulong GetUInt64()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (ulong)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (ulong)d_value;
            case VariantType.STRING:
                return (ulong)GameConvert.IntConvert(str_value); 
            default:
                return (ulong)0;
        }
    }

    public byte GetUInt8()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (byte)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (byte)d_value;
            case VariantType.STRING:
                return (byte)GameConvert.IntConvert(str_value);
            default:
                return (byte)0;
        }
    }

    public byte GetByte()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return (byte)n_value;
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return (byte)d_value;
            case VariantType.STRING:
                return (byte)GameConvert.IntConvert(str_value);
            default:
                return (byte)0;
        }
    }

    public override string ToString()
    {
        switch (type)
        {
            case VariantType.BOOL:
            case VariantType.INT8:
            case VariantType.INT16:
            case VariantType.INT32:
            case VariantType.INT64:
            case VariantType.UINT8:
            case VariantType.UINT16:
            case VariantType.UINT32:
            case VariantType.UINT64:
                return n_value.ToString();
            case VariantType.FLOAT:
            case VariantType.DOUBLE:
                return d_value.ToString();
            case VariantType.STRING:
                return str_value;

            default:
                if (null != obj_value)
                    return obj_value.ToString();
                else
                    return string.Empty;
        }
    }

    public static bool operator !=(Variant a, Variant b)
    {
        return !VariantEqual(a, b);
    }

    public static bool operator ==(Variant a, Variant b)
    {
        return VariantEqual(a, b);
    }

    static bool VariantEqual(Variant a, Variant b)
    {
        if (a.type == b.type)
        {
            switch (a.type)
            {
                case VariantType.BOOL:
                case VariantType.INT8:
                case VariantType.INT16:
                case VariantType.INT32:
                case VariantType.INT64:
                case VariantType.UINT8:
                case VariantType.UINT16:
                case VariantType.UINT32:
                case VariantType.UINT64:
                    return a.n_value == b.n_value;
                case VariantType.FLOAT:
                case VariantType.DOUBLE:
                    return a.d_value == b.d_value;
                case VariantType.STRING:
                    return a.str_value == b.str_value;
                case VariantType.OBJECT:
                    return a.obj_value == b.obj_value;

                default:
                    return false;
            }
        }

        if (a.IsInt() && b.IsInt())
        {
            return a.n_value == b.n_value;
        }

        if (a.IsFloatNumber() && b.IsFloatNumber())
        {
            return a.d_value == b.d_value;
        }

        if (a.type == VariantType.BOOL && b.type == VariantType.BOOL)
        {
            return a.n_value == b.n_value;
        }

        if (a.type == VariantType.OBJECT && b.type == VariantType.OBJECT)
        {
            return a.obj_value == b.obj_value;
        }

        if (a.type == VariantType.STRING && b.type == VariantType.STRING)
        {
            return a.str_value == b.str_value;
        }



        return false;
    }

    public override bool Equals(object obj)
    {
        Variant b = (Variant)obj;

        return VariantEqual(this, b);
    }

    public override int GetHashCode()
    {
        if (type == VariantType.BOOL || IsInt32())
        {
            return (int)n_value;
        }

        return base.GetHashCode();
    }
}
